P09-1. Grafana Loki와 서비스 메시 로그 아키텍처 - 이론 개념
Part 09 (이론): LGTM 로그 파이프라인 — Grafana Loki와 서비스 메시 로그 아키텍처 (Telemetry · LogQL)
- 서비스 메시의 방대한 액세스 로그를 저장할 때 왜 전통적인 Elasticsearch(ELK) 의 전문 색인(Full-Text Indexing) 방식이 비용·메모리 폭발을 부르는지, 그리고
Grafana Loki가 "메타데이터 라벨만 색인" 하여 이를 어떻게 해결했는지 설명할 수 있다. - Loki를 설계할 때 절대 어기면 안 되는 제1원칙인 고카디널리티(High Cardinality) 라벨 금지 원칙을 이해한다.
- Istio의
TelemetryAPI 를 사용해Waypoint Proxy의 L7 Envoy 액세스 로그를 동적으로 활성화하고, 실무 장애 진단의 핵심 암호인 Envoy 응답 플래그(UO,UT,UF,UH,NR) 를 해독할 수 있다. ztunnel의 L4 HBONE 로그(logfmt형식)와Waypoint의 L7 액세스 로그를LogQL(| logfmt,| json,|= " 필터 ")로 자유자재로 검색·집계하는 문법을 익힌다.
1. 왜 서비스 메시 로그에는 Elasticsearch 대신 Grafana Loki를 쓸까?
서비스 메시(ztunnel + Waypoint)를 도입하면 클러스터 안에서 일어나는 모든 통신이 투명하게 기록됩니다.
하지만 동전의 양면처럼 "로그의 양이 이전보다 2~3배 이상 많아진다" 는 현실적인 문제가 뒤따릅니다.
이 방대한 로그를 어디에 저장할 것인가를 두고 과거의 Elasticsearch(ELK 스택) 와 현대의 Grafana Loki 는 정반대의 철학을 택했습니다.
flowchart TB
subgraph ELK["1. 전통적 방식 · Elasticsearch (전문 색인 / Full-Text Indexing)"]
L1["로그 1줄 도착:
2026-10-25 GET /orders 503 UO ip=10.244.2.7"] --> SPLIT["모든 단어를 낱개로 쪼개어 거대한 색인 사전(Inverted Index) 생성!
('GET', 'orders', '503', 'UO', '10.244.2.7' 전부 인덱싱)"]
SPLIT --> COST1["단점: 원본 로그보다 색인 파일이 더 커짐!
수십 GB의 JVM 힙 메모리와 비싼 SSD 디스크 필수!"]
end
subgraph LOKI["2. 클라우드 네이티브 표준 · Grafana Loki (라벨 색인 + 원본 압축)"]
L2["로그 1줄 도착:
2026-10-25 GET /orders 503 UO ip=10.244.2.7"] --> LABEL["오직 겉봉투의 라벨(Label)만 색인!
{namespace='part1-demo', pod='waypoint-...'}"]
LABEL --> CHUNK["로그 본문은 쪼개지 않고 초고압축(Chunk)하여
저렴한 스토리지(디스크/S3)에 그대로 보관!
(메모리 사용량 1/10 이하!)"]
endGrafana Loki, Log Stream, 그리고 High Cardinality(고카디널리티)의 함정
Grafana Loki("Like Prometheus, but for Logs"):- Grafana Labs가 Prometheus의 설계 철학을 그대로 본떠 만든 로그 저장소입니다. 로그 본문 안의 글자(
ip=10.244.2.7등)는 미리 색인하지 않고, 오직 그 로그가 어느 파드에서 나왔는지를 나타내는 쿠버네티스 라벨(namespace,app,pod,container)만 색인합니다.
- Grafana Labs가 Prometheus의 설계 철학을 그대로 본떠 만든 로그 저장소입니다. 로그 본문 안의 글자(
Log Stream(로그 스트림):- 똑같은 라벨 조합(예:
{namespace="part1-demo", app="order-api"})을 가진 로그 줄들이 시간 순서대로 차곡차곡 쌓이는 하나의 '로그 서랍장'입니다.
- 똑같은 라벨 조합(예:
Cardinality(카디널리티 = 값의 종류 개수)와 설계 주의사항:- 라벨에 들어갈 수 있는 값의 가짓수를 카디널리티라고 합니다. 예를 들어
namespace라벨은 많아야 수십 개(저카디널리티)지만,client_ip나request_id,user_id는 수백만 개(고카디널리티)가 될 수 있습니다. - Loki 제1원칙: 만약
request_id같은 고카디널리티 값을 Loki의 인덱스 라벨({...})에 넣으면 로그 서랍장(Stream)이 수백만 개가 만들어져 Loki가 느려집니다! - 따라서 인덱스 라벨(
{...})에는namespace,app,pod처럼 종류가 적은 것만 넣고,request_id나src.addr,response_code같은 상세 필드는 로그 본문 안에 둔 채 쿼리할 때 파이프(|= "...",| logfmt,| json)로 실시간 추출하는 것이 Loki의 핵심 비결입니다!
- 라벨에 들어갈 수 있는 값의 가짓수를 카디널리티라고 합니다. 예를 들어
2. Ambient Mesh의 두 가지 핵심 로그 해부 (ztunnel L4 vs Waypoint L7)
Ambient Mesh에서는 데이터 플레인이 2계층으로 나뉘어 있으므로, 로그도 두 곳에서 서로 다른 깊이(L4 vs L7)로 남습니다!
2-1. 1층 보안요원 ztunnel의 L4 HBONE 액세스 로그 (logfmt 형식)
ztunnel 데몬셋은 별도 설정 없이도 기본적으로 모든 연결이 끝날 때마다 아래와 같은 키=값 (logfmt) 형식의 정교한 L4 로그를 남깁니다(우리가 Part 03, 04에서 봤던 바로 그 로그입니다!).
2026-10-25T07:05:06.210527Z info access connection complete
src.addr=10.244.2.7:56890
src.workload="study-client"
src.namespace="part1-demo"
src.identity="spiffe://cluster.local/ns/part1-demo/sa/default"
dst.addr=10.244.1.8:15008
dst.hbone_addr=10.244.1.8:8080
dst.service="order-api.part1-demo.svc.cluster.local"
dst.workload="order-api-86d8bb68dd-xh5rm"
direction="outbound"
bytes_sent=174 bytes_recv=1304 duration="63ms"
- 장점:
키=값형태로 깔끔하게 정렬되어 있어서, Loki에서| logfmt파이프 하나만 붙이면src_workload,src_identity,dst_service,bytes_sent,error가 전부 검색 가능한 변수로 자동 변환됩니다!
2-2. 2층 컨시어지 Waypoint Proxy의 L7 Envoy 액세스 로그 (Telemetry API로 활성화!)
반면 Waypoint Proxy(Envoy)는 디스크 부하를 줄이기 위해 기본 설치 상태에서는 성공한 일반 HTTP 요청 로그를 일일이 화면(stdout)에 찍지 않습니다.
이를 켜기 위해 Istio가 제공하는 표준 리소스가 바로 kind: Telemetry 입니다!
apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
name: mesh-logging
namespace: part1-demo
spec:
accessLogging:
- providers:
- name: envoy
위 YAML 9줄만 적용하면, 파드 재시작 없이 그 즉시 Waypoint가 모든 HTTP 요청의 메서드(GET), 경로(/orders), 상태 코드(200, 403, 503), 응답 시간(ms), 그리고 가장 중요한 '응답 플래그(RESPONSE_FLAGS)' 를 실시간으로 출력합니다!
3. 실무 트러블슈팅 치트키: Envoy 응답 플래그 (RESPONSE_FLAGS) 완벽 해독표
일반 웹 서버 로그는 단순히 HTTP 503이라고만 찍혀서, 도대체 왜 503이 났는지(서킷 브레이커 때문인지, 파드가 죽어서인지, 연결이 끊겨서인지) 알 수가 없습니다.
반면 Istio(Waypoint/Envoy) 액세스 로그는 상태 코드 바로 옆에 2글자짜리 비밀 암호(RESPONSE_FLAGS) 를 함께 찍어 줍니다! 이 표 하나만 알고 있어도 실무 장애 원인의 90%를 5초 만에 판독할 수 있습니다.
| 응답 플래그 | 영문 풀네임 | 한국어 의미 및 실제 발생 원인 | 관련 파트 |
|---|---|---|---|
- |
None (정상) | 아무런 프록시 에러 없이 백엔드 응답이 정상 처리됨 | 전체 |
UO |
Upstream Overflow | 서킷 브레이커 작동! (DestinationRule의 maxConnections / http1MaxPendingRequests 한도를 초과하여 프록시가 즉시 차단함) |
Part 05 |
UT |
Upstream Request Timeout | 백엔드 파드가 지정된 timeout 시간 내에 응답하지 않아 프록시가 연결을 끊음 (504 Gateway Timeout) |
Part 05 / 12 |
UF |
Upstream Connection Failure | 백엔드 파드나 외부 VM(study-pg-vm)의 포트가 닫혀 있거나 네트워크 연결 자체에 실패함 |
Part 06 |
UH |
No Healthy Upstream | 목적지 서비스에 살아있는(Healthy) 파드가 0개이거나, OutlierDetection에 의해 모든 파드가 격리됨 |
Part 05 |
NR |
No Route Configured | 요청한 도메인이나 경로에 매칭되는 HTTPRoute / VirtualService 규칙이 없음 (404) |
Part 05 |
DC |
Downstream Connection Termination | 서버가 응답을 주기도 전에 클라이언트(curl/브라우저)가 먼저 답답해서 연결을 끊어버림! |
Part 12 |
4. LogQL (Loki 쿼리 언어) 2단계 파이프라인 마스터하기
Prometheus에 PromQL이 있다면, Loki에는 LogQL 이 있습니다. 구조가 놀라울 정도로 직관적입니다.
{namespace="part1-demo", pod=~"waypoint.*"} |= "503" | logfmt
└─────────────── 1단계: 서랍장 고르기 ──────────────┘ └─── 2단계: 본문 필터링 & 파싱 ───┘
- 1단계 · 로그 스트림 선택기 (
{라벨="값"}):{namespace="part1-demo"}:part1-demo네임스페이스의 모든 파드 로그 서랍장을 엽니다.{namespace="istio-system", pod=~"ztunnel.*"}: 정규식(=~)으로 이름이ztunnel로 시작하는 모든 파드의 로그 서랍장을 엽니다.
- 2단계 · 본문 필터 및 파서 파이프라인 (
| ...):|= "문자열": 본문에 해당 문자열이 포함된 줄만 초고속으로 걸러냅니다 (예:|= "UO"또는|= "503").!= "문자열": 해당 문자열이 들어간 줄(예: 시끄러운 헬스체크 로그!= "/healthz")을 제외합니다.| logfmt또는| json: 로그 줄을키=값이나 JSON으로 자동 분해하여duration,src_workload,response_code같은 필드로 조건 검색을 할 수 있게 만듭니다!- 메트릭 변환 함수 (
count_over_time):
sum(count_over_time({namespace="part1-demo"} |= "503" [5m]))처럼 로그 개수를 숫자로 세어 실시간 에러 그래프로 그릴 수도 있습니다!
🔗 다음 단계 (실습 검증)
이론으로 배운 LGTM 로그 파이프라인을 이제 클러스터에 직접 구축합니다!
monitoring네임스페이스에Grafana Loki(3.4.2) 를 배포하고,Grafana Alloy설정에 쿠버네티스 파드 로그 수집 파이프라인(loki.source.kubernetes→loki.write)을 추가합니다.- Istio
TelemetryAPI 로Waypoint Proxy의 L7 액세스 로그를 켠 뒤, - 정상 요청과 서킷 브레이커(
503 UO) 요청을 발생시키고 Loki HTTP API에 직접LogQL쿼리를 날려ztunnel의 HBONE 로그와Waypoint의 Envoy 로그(UO플래그 포함)가 1초 만에 검색되는 것을 실측합니다!
네비게이션
| 이전 | 목록 | 다음 |
|---|---|---|
| ◀ Part 08 실습 | 목록으로 | Part 09 실습 ▶ |